package ink.ykb.demo.permission.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import ink.ykb.demo.config.CodeConstant;
import ink.ykb.demo.config.MessageUtil;
import ink.ykb.demo.config.ServiceException;
import ink.ykb.demo.permission.dao.UserRoleDao;
import ink.ykb.demo.permission.entity.Role;
import ink.ykb.demo.permission.entity.Rule;
import ink.ykb.demo.permission.entity.UserRole;

@Service
public class UserRoleServiceImpl implements UserRoleService{

  @Autowired
  private MessageUtil messageUtil;
  @Autowired
  private UserRoleService self;
  @Autowired
  private UserRoleDao userRoleDao;
  @Autowired
  private RoleService roleService;
  @Autowired
  private RoleRuleService roleRuleService;

  @Caching(evict = {
      @CacheEvict(cacheNames = {"userRoleListByUserId"}, key = "#userRole.userId"),
      @CacheEvict(cacheNames = {"userRoleListByRoleId"}, key = "#userRole.roleId")
  })
  @Transactional
  @Override
  public void add(UserRole userRole) {
    if(userRole == null) {
      throw new ServiceException(CodeConstant.PARAMS_EMPTY,messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
    }
    if(userRole.getUserId() == null) {
      throw new ServiceException(CodeConstant.PARAMS_EMPTY,messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
    }
    if(userRole.getRoleId() == null) {
      throw new ServiceException(CodeConstant.PARAMS_EMPTY,messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
    }
    
    UserRole dbUserRole = userRoleDao.getUserRoleAndLock(userRole.getUserId(),userRole.getRoleId());
    if(dbUserRole != null) {
      throw new ServiceException(CodeConstant.MUS_104,messageUtil.getMsg(CodeConstant.MUS_104));
    }
    
    userRole.setCreateTime(new Date());
    userRoleDao.insert(userRole);
    
  }

  @Caching(evict = {
      @CacheEvict(cacheNames = {"userRole"},key = "#userRoleId"),
      @CacheEvict(cacheNames = {"userRoleListByUserId"}, allEntries=true),
      @CacheEvict(cacheNames = {"userRoleListByRoleId"}, allEntries=true),
      @CacheEvict(cacheNames = {"userRoleListByUserIdAndRoleId"}, allEntries=true)
  })
  @Transactional
  @Override
  public void delete(Long userRoleId) {
    UserRole dbUserRole = userRoleDao.getUserRole(userRoleId);
    if(dbUserRole == null) {
      throw new ServiceException(CodeConstant.MUS_105,messageUtil.getMsg(CodeConstant.MUS_105));
    }
    
    userRoleDao.deleteById(dbUserRole.getId());
  }
  
  @Caching(evict = {
      @CacheEvict(cacheNames = {"userRole"}, allEntries=true),
      @CacheEvict(cacheNames = {"userRoleListByUserId"}, key = "#userId"),
      @CacheEvict(cacheNames = {"userRoleListByRoleId"}, key = "#roleId"),
      @CacheEvict(cacheNames = {"userRoleListByUserIdAndRoleId"}, key = "#userId +'-'+#roleId")
  })
  @Transactional
  @Override
  public void deleteByUserIdAndRoleId(Long userId,Long roleId) {
    
    UserRole dbUserRole = userRoleDao.getUserRoleAndLock(userId,roleId);
    if(dbUserRole == null) {
      throw new ServiceException(CodeConstant.MUS_105,messageUtil.getMsg(CodeConstant.MUS_105));
    }
    
    userRoleDao.deleteById(dbUserRole.getId());
  }
  
  @Cacheable(cacheNames = "userRoleListByUserId", key = "#userId")
  @Override
  public List<UserRole> getUserRoleListByUserId(Long userId) {
	 return userRoleDao.getUserRoleByUserId(userId);
  }
  
  @Cacheable(cacheNames = "userRoleListByRoleId", key = "#roleId")
  @Override
  public List<UserRole> getUserRoleListByRoleId(Long roleId) {
	  return userRoleDao.getUserRoleByRoleId(roleId);
  }

  @Cacheable(cacheNames = "userRole", key = "#userRoleId")
  @Override
  public UserRole getUserRole(Long userRoleId) {
    return userRoleDao.getUserRole(userRoleId);
  }
  
  @Cacheable(cacheNames = "userRoleListByUserIdAndRoleId", key = "#userId +'-'+#roleId")
  @Override
  public UserRole getUserRole(Long userId, Long roleId) {
	  return userRoleDao.getUserRole(userId, roleId);
  }
  
  @Override
  public List<Role> getUserRoleList(Long userId) {
    List<UserRole> list = self.getUserRoleListByUserId(userId);
    List<Role> roleList = roleService.getRoleList();
    
    List<Role> userRoleList = new ArrayList<>();
    
    roleList.forEach(r -> {
      for (UserRole ur : list) {
        if(ur.getRoleId().equals(r.getId())) {
          userRoleList.add(r);
          break;
        }
      }
    });
    
    return userRoleList;
  }

  @Override
  public List<Rule> getUserRuleList(Long userId) {
    List<Role> list = self.getUserRoleList(userId);
    
    Set<Rule> userRuleSet = new HashSet<>();
    
    list.forEach(r -> {
      List<Rule> roleRuleList = roleRuleService.getRuleListByRole(r.getId());
       for (Rule rule : roleRuleList) {
         userRuleSet.add(rule);
       }
    });
    List<Rule> result = new ArrayList<>(userRuleSet);
	return result;
  }
  
  @Override
  public List<Rule> getUserRuleList(Long userId,Integer type) {
	  List<Role> list = self.getUserRoleList(userId);
	  
	  Set<Rule> userRuleSet = new HashSet<>();
	  
	  list.forEach(r -> {
		  List<Rule> roleRuleList = roleRuleService.getRuleListByRole(r.getId());
		  for (Rule rule : roleRuleList) {
			  if(type.equals(rule.getType())) {
				  userRuleSet.add(rule);
			  }
		  }
	  });
	  List<Rule> result = new ArrayList<>(userRuleSet);
	  return result;
  }
  
  @Caching(evict = {
	      @CacheEvict(cacheNames = {"userRoleListByUserId"}, key = "#userId"),
	      @CacheEvict(cacheNames = {"userRoleListByRoleId"},allEntries = true),
  })
  @Transactional
  @Override
  public void addBatchUserRole(Long userId,String roleIds) {
    String[] list = roleIds.split(",");
    for (String roleId : list) {
      if(StringUtils.isNotBlank(roleId)) {
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(Long.valueOf(roleId));
        this.add(userRole);
      }
    }
  }
  
  @Caching(evict = {
		  @CacheEvict(cacheNames = {"userRole"},allEntries = true),
		  @CacheEvict(cacheNames = {"userRoleListByUserId"}, key = "#userId"),
	      @CacheEvict(cacheNames = {"userRoleListByRoleId"},allEntries = true),
		  @CacheEvict(cacheNames = {"userRoleListByUserIdAndRoleId"},allEntries = true)
  })
  @Transactional
  @Override
  public void deleteBatchUserRole(Long userId,String roleIds) {
    String[] list = roleIds.split(",");
    for (String roleId : list) {
      if(StringUtils.isNotBlank(roleId)) {
        this.deleteByUserIdAndRoleId(userId, Long.valueOf(roleId));
      }
    }
  }

  @Caching(evict = {
		  @CacheEvict(cacheNames = {"userRole"},allEntries = true),
		  @CacheEvict(cacheNames = {"userRoleListByUserId"}, key = "#userId"),
	      @CacheEvict(cacheNames = {"userRoleListByRoleId"},allEntries = true),
	      @CacheEvict(cacheNames = {"userRoleListByUserIdAndRoleId"},allEntries = true)
  })
  @Transactional
  @Override
  public void updateBatchUserRole(Long userId, String roleIds) {
    
    //此用户原来拥有的角色
    Set<Long> oldRoleIdSet = new HashSet<>();
    List<UserRole> oldUserRoleList = this.getUserRoleListByUserId(userId);
    oldUserRoleList.forEach(ur -> {
      oldRoleIdSet.add(ur.getRoleId());
    });
    
    //此用户现在拥有的新角色
    Set<Long> newRoleIdSet = new HashSet<>();
    
    //新增的角色
    Set<Long> addRoleIdSet = new HashSet<>();
    
    List<Role> roleList = roleService.getRoleList();
    String[] roleIdList = roleIds.split(",");
    for (String roleId : roleIdList) {
      if(StringUtils.isNotBlank(roleId)) {
        for (Role role : roleList) {
          if(role.getId().equals(Long.valueOf(roleId))) {
            newRoleIdSet.add(role.getId());
            addRoleIdSet.add(role.getId());
            break;
          }
        }
      }
    }
    
    //新增部分，求差集
    addRoleIdSet.removeAll(oldRoleIdSet);
    
    //删除部分，求差集
    oldRoleIdSet.removeAll(newRoleIdSet);
    
    //新增
    for (Long roleId : addRoleIdSet) {
      UserRole userRole = new UserRole();
      userRole.setUserId(userId);
      userRole.setRoleId(roleId);
      this.add(userRole);
    }
    
    //删除
    for (Long roleId : oldRoleIdSet) {
      this.deleteByUserIdAndRoleId(userId, roleId);
    }
    
  }
  
  
}
